package com.demo.supplier.aspect;

import cn.hutool.core.util.StrUtil;
import com.demo.supplier.annotation.LoginLog;
import com.demo.supplier.config.RabbitMqConfig;
import com.demo.supplier.dto.LoginDto;
import com.demo.supplier.entity.SysUser;
import com.demo.supplier.exception.BusinessException;
import com.demo.supplier.vo.LoginVo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 登录日志AOP切面
 * 使用环绕通知拦截登录方法，通过RabbitMQ异步记录登录成功和失败日志
 * 
 * @author demo
 */
@Slf4j
@Aspect
@Component
public class LoginLogAspect {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 定义切点：标记了@LoginLog注解的方法
     */
    @Pointcut("@annotation(com.demo.supplier.annotation.LoginLog)")
    public void loginLogPointcut() {}

    /**
     * 环绕通知：拦截登录方法，记录登录日志
     */
    @Around("loginLogPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        // 获取方法信息和注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LoginLog annotation = method.getAnnotation(LoginLog.class);
        
        // 提取用户名
        String username = extractUsername(joinPoint.getArgs());
        
        try {
            // 执行目标方法（登录逻辑）
            Object result = joinPoint.proceed();
            
            // 登录成功，记录成功日志
            recordLoginSuccess(username, result, annotation, startTime);
            
            return result;
            
        } catch (BusinessException e) {
            // 业务异常（登录失败），记录失败日志
            recordLoginFailure(username, e.getMessage(), annotation, startTime);
            throw e;
            
        } catch (Exception e) {
            // 系统异常，记录失败日志
            recordLoginFailure(username, "系统异常: " + e.getMessage(), annotation, startTime);
            throw e;
        }
    }

    /**
     * 从方法参数中提取用户名
     */
    private String extractUsername(Object[] args) {
        if (args == null || args.length == 0) {
            return "unknown";
        }
        
        for (Object arg : args) {
            if (arg instanceof LoginDto) {
                LoginDto loginDto = (LoginDto) arg;
                return StrUtil.isNotBlank(loginDto.getUsername()) ? loginDto.getUsername() : "unknown";
            }
        }
        
        return "unknown";
    }

    /**
     * 记录登录成功日志
     */
    private void recordLoginSuccess(String username, Object result, LoginLog annotation, long startTime) {
        try {
            // 获取HTTP请求上下文
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                log.warn("无法获取HTTP请求上下文，跳过登录成功日志记录");
                return;
            }

            HttpServletRequest request = attributes.getRequest();
            
            // 从返回结果中提取用户信息
            SysUser user = extractUserFromResult(result);
            
            // 构建登录日志
            com.demo.supplier.entity.LoginLog loginLog = new com.demo.supplier.entity.LoginLog();
            loginLog.setUserId(user != null ? user.getId() : null);
            loginLog.setUsername(username);
            loginLog.setLoginType(annotation.type());
            loginLog.setLoginResult("SUCCESS");
            loginLog.setIp(getClientIp(request));
            loginLog.setUserAgent(request.getHeader("User-Agent"));
            loginLog.setCreateTime(LocalDateTime.now());

            // 通过RabbitMQ异步保存日志
            rabbitTemplate.convertAndSend(
                    RabbitMqConfig.LOG_TOPIC_EXCHANGE,
                    RabbitMqConfig.LOGIN_LOG_ROUTING_KEY,
                    loginLog
            );
            
            long executionTime = System.currentTimeMillis() - startTime;
            log.debug("发送登录成功日志到MQ: 用户={}, 耗时={}ms", username, executionTime);
            
        } catch (Exception e) {
            log.error("发送登录成功日志到MQ失败: username={}", username, e);
        }
    }

    /**
     * 记录登录失败日志
     */
    private void recordLoginFailure(String username, String failureReason, LoginLog annotation, long startTime) {
        try {
            // 获取HTTP请求上下文
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                log.warn("无法获取HTTP请求上下文，跳过登录失败日志记录");
                return;
            }

            HttpServletRequest request = attributes.getRequest();
            
            // 构建登录日志
            com.demo.supplier.entity.LoginLog loginLog = new com.demo.supplier.entity.LoginLog();
            loginLog.setUserId(null); // 登录失败时用户ID为空
            loginLog.setUsername(username);
            loginLog.setLoginType(annotation.type());
            loginLog.setLoginResult("FAILURE");
            loginLog.setFailureReason(failureReason);
            loginLog.setIp(getClientIp(request));
            loginLog.setUserAgent(request.getHeader("User-Agent"));
            loginLog.setCreateTime(LocalDateTime.now());

            // 通过RabbitMQ异步保存日志
            rabbitTemplate.convertAndSend(
                    RabbitMqConfig.LOG_TOPIC_EXCHANGE,
                    RabbitMqConfig.LOGIN_LOG_ROUTING_KEY,
                    loginLog
            );
            
            long executionTime = System.currentTimeMillis() - startTime;
            log.debug("发送登录失败日志到MQ: 用户={}, 原因={}, 耗时={}ms", username, failureReason, executionTime);
            
        } catch (Exception e) {
            log.error("发送登录失败日志到MQ失败: username={}, reason={}", username, failureReason, e);
        }
    }

    /**
     * 从返回结果中提取用户信息
     */
    private SysUser extractUserFromResult(Object result) {
        if (result instanceof LoginVo) {
            LoginVo loginVo = (LoginVo) result;
            if (loginVo.getUserInfo() != null) {
                SysUser user = new SysUser();
                user.setId(loginVo.getUserInfo().getId());
                user.setUsername(loginVo.getUserInfo().getUsername());
                return user;
            }
        }
        return null;
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StrUtil.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip != null ? ip : "unknown";
    }
}